﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.Xml.Serialization;
using System.IO;
using System.Text;

namespace DragDraw.Service
{
    [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession, ConcurrencyMode = ConcurrencyMode.Multiple)]
    public class UserDuplexService : IUserDuplexService
    {
        private static Object syncObj = new object();
        //userID map to callback
        private static Dictionary<string, IUserServiceCallback> ClientsDict = new Dictionary<string, IUserServiceCallback>();

        //widgetID map to users
        private static Dictionary<int, List<string>> widgetsDict = new Dictionary<int, List<string>>();

        // 会话的保存,Key 为SessionName;
        private static Dictionary<string, SessionData> SessionDict = new Dictionary<string, SessionData>();
        IUserServiceCallback ClientCallback = null;

        // Add more operations here and mark them with [OperationContract]
        public void JoinSession(Message sessionData)
        {
            try
            {
                if (ClientCallback == null)
                    ClientCallback = OperationContext.Current.GetCallbackChannel<IUserServiceCallback>();

                SessionData mySession = MsgToSession(sessionData);

                lock (syncObj)
                {
                    if (string.IsNullOrEmpty(mySession.UserID) == false)
                    {
                        if (ClientsDict.ContainsKey(mySession.UserID) == false)
                        {
                            ClientsDict.Add(mySession.UserID, ClientCallback);
                        }
                        else // client callback changed, maybe relogin!!
                        {
                            ClientsDict[mySession.UserID] = ClientCallback;
                        }
                    }

                    // 往WidgetsDict里面添加widgetID与userID对应项
                    // 如果WidgetsDict已经有key:widgetID,往List<string>里添加userID
                    if (widgetsDict.ContainsKey(mySession.WidgetID))
                    {
                        List<string> userIDList = widgetsDict[mySession.WidgetID];
                        // widgetList里的userID不允许重复
                        if (!userIDList.Contains(mySession.UserID))
                            userIDList.Add(mySession.UserID);
                    }
                    // 如果WidgetsDict里面没有Key:widgetID,则添加新的Key
                    else
                    {
                        List<string> userIDList = new List<string>();
                        userIDList.Add(mySession.UserID);
                        widgetsDict.Add(mySession.WidgetID, userIDList);
                    }

                    // if session is new, add
                    if (!SessionDict.ContainsKey(SessionUID(mySession.WidgetID, mySession.SessionName)))
                    {
                        SessionDict.Add(SessionUID(mySession.WidgetID, mySession.SessionName), mySession);
                    }
                    else// it's already exsited, send
                    {
                        //SendMsgTo(SessionDict[SessionUID(mySession.WidgetID, mySession.SessionName)], mySession.UserID);
                    }
                }
            }
            catch(Exception e)
            {
                LogException("joinSession", e);
            }
        }

        public void UpdateSession(Message sessionData)
        {
            try
            {
                if (ClientCallback == null)
                    ClientCallback = OperationContext.Current.GetCallbackChannel<IUserServiceCallback>(); ;

                SessionData mySession = MsgToSession(sessionData);

                List<string> userIDList = null;
                bool isUpdate = false;
                lock (syncObj)
                {
                    // 如果SessionDict里面有保存旧的SessionData，则进行更新
                    if (SessionDict.ContainsKey(SessionUID(mySession.WidgetID, mySession.SessionName)))
                    {
                        // 提取所有用旧SessionData的用户ID
                        userIDList = widgetsDict[mySession.WidgetID];

                        //　更新SessionDict里的SessionData
                        SessionDict[SessionUID(mySession.WidgetID, mySession.SessionName)] = mySession;
                        isUpdate = true;
                    }
                }

                if (isUpdate)
                {
                    //　通知所有用户
                    int i = 0;
                    while (i < userIDList.Count)
                    {
                        if (SendMsgTo(mySession, userIDList[i]))//success
                            i++;
                        //if failed, then userIDList[i] has delete current user
                        //and i should not increase
                    }
                }
            }
            catch(Exception e)
            {
                LogException("updateSession", e);
            }
        }

        public void SyncSession(Message syncFlag)
        {
            try
            {
                if (ClientCallback == null)
                    ClientCallback = OperationContext.Current.GetCallbackChannel<IUserServiceCallback>();
                StringWriter strWriter = new StringWriter();
                SessionData data = new SessionData();
                data.SessionName = "";
                data.UserID = "";
                data.Value = "";
                data.WidgetID = -1;
                XmlSerializer xs = new XmlSerializer(typeof(SessionData));
                xs.Serialize(strWriter, data);
                Message returnMessage = Message.CreateMessage(MessageVersion.Soap11, "Silverlight/IUserDuplexService/OnUpdateSession", strWriter.ToString());
                try
                {
                    ClientCallback.OnUpdateSession(returnMessage);
                }
                catch// this channel is totally dead, delete
                {
                }
            }
            catch (Exception e)
            {
                LogException("syncSession", e);
            }
        }
        //　通知所有用户
        private bool SendMsgTo(SessionData data, string userID)
        {
            try
            {
                if (ClientsDict.ContainsKey(userID))
                {
                    Message msg = SessionToUpdateMsg(data);
                    IUserServiceCallback callback = ClientsDict[userID];
                    try
                    {
                        callback.OnUpdateSession(msg);
                    }
                    catch// this channel is inactive, delete
                    {
                        lock (syncObj)
                        {
                            ClientsDict.Remove(userID);
                            widgetsDict[data.WidgetID].Remove(userID);
                        }
                        return false;
                    }
                }
            }
            catch(Exception e)
            {
                LogException("sendMsgTo", e);
            }
            return true;
        }

        private string SessionUID(int widgetID, string sessionName)
        {
            return widgetID.ToString() + "_" + sessionName;
        }

        private SessionData MsgToSession(Message msg)
        {
            try
            {
                SessionData ret;
                string str = msg.GetBody<string>();
                XmlSerializer serial = new XmlSerializer(typeof(SessionData));
                StringReader strRead = new StringReader(str);
                ret = (SessionData)serial.Deserialize(strRead);
                return ret;
            }
            catch (Exception e)
            {
                LogException("MsgToSession", e);
            }
            return null;
        }

        private Message SessionToUpdateMsg(SessionData data)
        {
            try
            {
                Message returnMessage;
                StringWriter strWriter = new StringWriter();
                XmlSerializer serial = new XmlSerializer(typeof(SessionData));
                serial.Serialize(strWriter, SessionDict[SessionUID(data.WidgetID, data.SessionName)]);
                returnMessage = Message.CreateMessage(MessageVersion.Soap11, "Silverlight/IUserDuplexService/OnUpdateSession", strWriter.ToString());
                return returnMessage;
            }
            catch(Exception e)
            {
                LogException("sessionToUpdateMsg", e);
            }
            return null;
        }

        private void LogException(string funcName, Exception e)
        {
            int i = 0;
            Exception err = e;
            string msg = funcName + "出现异常：\n";
            while (err != null)
            {
                msg += "异常等级" + i.ToString() + " : " + err.Message + "\n";
                err = err.InnerException;
                i++;
            }
            ErrorManager.Instance.LogMessage(msg);
        }
    }
}
